// takes view model and collection name
// iterates over collection items and updates its properties if they have source
ko.utils.updateMembersWithSource = function (viewModel, collectionName, itemEquality, memberToSource) {
    itemEquality = itemEquality || function (item, reference) {
        return item && reference && item.Value() === reference.Value();
    };

    memberToSource = memberToSource || function (member) {
        return member + "Source";
    };

    var collection = viewModel[collectionName]();
    for (var i = collection.length; i--;) {
        var item = collection[i];
        for (var member in item) {
            if (item.hasOwnProperty(member)) {
                var source = viewModel[memberToSource(member)];
                if (source) {
                    var memberValue = ko.utils.unwrapObservable(item[member]);
                    var sourceValue = memberValue
                        ? ko.utils.arrayFirst(source(),
                        function (item) {
                            item = ko.utils.unwrapObservable(item);

                            return itemEquality(item, memberValue);
                        })
                        : null;
                    item[member] = ko.observable(sourceValue);
                }
            }
        }
    }

    return viewModel;
};

// lazy options - fills selects with options only after direct access (on focus)
// allows to speed up page initialization with tens of dropboxes in several times
ko.bindingHandlers.lazyOptions = {
    init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
        var context = this;
        var allBindings = allBindingsAccessor();
        var selectedOption = ko.utils.unwrapObservable(allBindings.value);
        var optionsCaption = ko.utils.unwrapObservable(allBindings.optionsCaption);
        if (selectedOption || optionsCaption) {
            valueAccessor = selectedOption ? function () {
                return [selectedOption];
            } : valueAccessor;
            ko.bindingHandlers.options.update.call(context, element, valueAccessor, allBindingsAccessor, viewModel);
        }

        element = $(element);
        var update = function () {
            var args = element.data("lazyargs");
            if (args) {
                ko.bindingHandlers.options.update.apply(context, args);
                element.data("lazyargs", null);
            }
        };
        element.focus(update);
    },
    update: function (element, valueAccessor, allBindingsAccessor, viewModel) {
        $(element).data("lazyargs", arguments);
    }
};

// binds href attribute to link to lingvopro search result for given spelling
ko.bindingHandlers.lingvo = {
    update: function (element, valueAccessor) {
        $(element).attr("href", "http://lingvopro.abbyyonline.com/ru/Search/en-ru/" +
            ko.utils.unwrapObservable(valueAccessor()));
    }
};

// binds text to line with comma separated elements
ko.bindingHandlers.commaSeparated = {
    update: function (element, valueAccessor) {
        $(element).text(ko.utils.unwrapObservable(valueAccessor()).join(", "));
    }
};

// triggers update event for element
ko.bindingHandlers.triggerUpdate = {
    update: function (element, valueAccessor) {
        //need to just access the observable to create the subscription
        ko.utils.unwrapObservable(valueAccessor());
        $(element).trigger("update");
    }
};